package com.weijian.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weijian.dal.entity.Course;
import com.weijian.dal.entity.Order;
import com.weijian.dal.entity.PaymentRecord;
import com.weijian.dal.enums.OrderStatus;
import com.weijian.dal.enums.PaymentMethod;
import com.weijian.dal.enums.PaymentStatus;
import com.weijian.dal.mapper.CourseMapper;
import com.weijian.dal.mapper.OrderMapper;
import com.weijian.dal.mapper.PaymentRecordMapper;
import com.weijian.dal.vo.OrderVO;
import com.weijian.service.OrderService;
import com.weijian.util.OrderLockUtil;
import com.weijian.util.RedisKeyUtil;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单服务实现类
 * 实现订单相关的业务逻辑
 *
 * @author weijian
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Resource
    private OrderLockUtil orderLockUtil;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private PaymentRecordMapper paymentRecordMapper;

    private static final int ORDER_EXPIRE_MINUTES = 30;

    /**
     * 创建订单 - 分布式锁防超卖 + Redis库存管理 + 30分钟过期
     *
     * @param userId   用户ID
     * @param courseId 课程ID
     * @return 创建的订单
     */
    @Override
    @Transactional
    public Order createOrder(Long userId, Long courseId) {
        String orderLockValue = null;
        try {
            // 1. 获取用户订单锁（防重复提交）
            orderLockValue = orderLockUtil.tryOrderCreateLock(userId, courseId);
            if (orderLockValue == null) {
                throw new RuntimeException("请勿重复提交订单");
            }

            // 2. 检查Redis库存
            String stockKey = RedisKeyUtil.getCourseStockKey(courseId);
            Integer stock = (Integer) redisTemplate.opsForValue().get(stockKey);
            if (stock == null) {
                // 库存未初始化，从数据库加载
                Course course = courseMapper.selectById(courseId);
                if (course == null) {
                    throw new RuntimeException("课程不存在");
                }
                stock = course.getStock();
                redisTemplate.opsForValue().set(stockKey, stock);
            }

            if (stock <= 0) {
                throw new RuntimeException("课程库存不足");
            }

            // 3. 获取课程信息
            Course course = getCourseInfo(courseId);
            if (course == null) {
                throw new RuntimeException("课程不存在");
            }

            if (course.getOnSale() != 1) {
                throw new RuntimeException("课程已停售");
            }

            // 4. Redis预减库存
            Long remainingStock = redisTemplate.opsForValue().decrement(stockKey);
            if (remainingStock < 0) {
                // 库存不足，恢复库存
                redisTemplate.opsForValue().increment(stockKey);
                throw new RuntimeException("课程库存不足");
            }

            // 5. 创建订单
            String orderNo = generateOrderNo();
            LocalDateTime expireTime = LocalDateTime.now().plusMinutes(ORDER_EXPIRE_MINUTES);

            Order order = new Order();
            order.setOrderNo(orderNo);
            order.setUserId(userId);
            order.setCourseId(courseId);
            order.setCourseTitle(course.getTitle());
            order.setCourseCover(course.getCoverImage());
            order.setCoursePrice(course.getPrice());
            order.setCourseLevel(course.getLevel());
            order.setCourseDuration(course.getDuration());
            order.setOriginalAmount(course.getPrice());
            order.setDiscountAmount(BigDecimal.ZERO);
            order.setPayAmount(course.getPrice());
            order.setStatus(OrderStatus.PENDING);
            order.setExpireTime(expireTime);
            order.setPaymentMethod(null);
            order.setPaymentTime(null);
            order.setCancelTime(null);
            order.setRefundTime(null);

            save(order);

            // 6. 设置订单过期（30分钟）
            String expireKey = RedisKeyUtil.getOrderExpireKey(orderNo);
            redisTemplate.opsForValue().set(expireKey, orderNo, ORDER_EXPIRE_MINUTES, TimeUnit.MINUTES);

            return order;

        } finally {
            // 7. 释放锁
            if (orderLockValue != null) {
                orderLockUtil.unlockOrderCreate(userId, courseId, orderLockValue);
            }
        }
    }

    /**
     * 查询用户订单列表
     *
     * @param userId 用户ID
     * @return 订单列表
     */
    @Override
    public List<OrderVO> getUserOrders(Long userId) {
        List<Order> orders = lambdaQuery()
                .eq(userId != null, Order::getUserId, userId)
                .eq(Order::getDeleted, 0)
                .orderByDesc(Order::getCreateTime)
                .list();

        return orders.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    /**
     * 订单支付
     *
     * @param orderId       订单ID
     * @param userId        用户ID
     * @param paymentMethod 支付方式
     */
    @Override
    @Transactional
    public void payOrder(Long orderId, Long userId, Integer paymentMethod) {
        Order order = getById(orderId);
        if (order == null) {
            throw new RuntimeException("订单不存在");
        }

        if (!order.getUserId().equals(userId)) {
            throw new RuntimeException("无权操作他人订单");
        }

        if (order.getStatus() != OrderStatus.PENDING) {
            throw new RuntimeException("订单状态异常，无法支付");
        }

        if (LocalDateTime.now().isAfter(order.getExpireTime())) {
            throw new RuntimeException("订单已过期");
        }

        // 1. 更新订单状态
        order.setStatus(OrderStatus.PAID);
        order.setPaymentMethod(PaymentMethod.fromCode(paymentMethod));
        order.setPaymentTime(LocalDateTime.now());
        updateById(order);

        // 2. 扣减数据库库存
        Course course = courseMapper.selectById(order.getCourseId());
        if (course != null) {
            course.setStock(course.getStock() - 1);
            course.setSoldCount(course.getSoldCount() + 1);
            courseMapper.updateById(course);
        }

        // 3. 创建支付记录
        String paymentNo = generatePaymentNo();
        PaymentRecord paymentRecord = new PaymentRecord();
        paymentRecord.setOrderNo(paymentNo);
        paymentRecord.setOrderNo(order.getOrderNo());
        paymentRecord.setUserId(userId);
        paymentRecord.setPaymentMethod(PaymentMethod.fromCode(paymentMethod));
        paymentRecord.setAmount(order.getPayAmount());
        paymentRecord.setStatus(PaymentStatus.SUCCESS);
        paymentRecord.setPayTime(LocalDateTime.now());
        paymentRecordMapper.insert(paymentRecord);

        // 4. 删除订单过期键
        String expireKey = RedisKeyUtil.getOrderExpireKey(order.getOrderNo());
        redisTemplate.delete(expireKey);
    }

    /**
     * 取消订单
     *
     * @param orderId 订单ID
     * @param userId  用户ID
     */
    @Override
    @Transactional
    public void cancelOrder(Long orderId, Long userId) {
        Order order = getById(orderId);
        if (order == null) {
            throw new RuntimeException("订单不存在");
        }

        if (!order.getUserId().equals(userId)) {
            throw new RuntimeException("无权操作他人订单");
        }

        if (order.getStatus() != OrderStatus.PENDING) {
            throw new RuntimeException("订单状态异常，无法取消");
        }

        // 1. 更新订单状态
        order.setStatus(OrderStatus.CANCELLED);
        order.setCancelTime(LocalDateTime.now());
        updateById(order);

        // 2. 恢复Redis库存
        String stockKey = RedisKeyUtil.getCourseStockKey(order.getCourseId());
        redisTemplate.opsForValue().increment(stockKey);

        // 3. 删除订单过期键
        String expireKey = RedisKeyUtil.getOrderExpireKey(order.getOrderNo());
        redisTemplate.delete(expireKey);
    }

    /**
     * 初始化课程库存到Redis（开发测试用）
     *
     * @param courseId 课程ID
     * @param stock    库存数量
     */
    @Override
    public void initCourseStock(Long courseId, Integer stock) {
        String stockKey = RedisKeyUtil.getCourseStockKey(courseId);
        redisTemplate.opsForValue().set(stockKey, stock);
    }

    /**
     * 获取课程信息（优先Redis缓存）
     *
     * @param courseId 课程ID
     * @return 课程信息
     */
    private Course getCourseInfo(Long courseId) {
        String courseKey = RedisKeyUtil.getCourseDetailKey(courseId);
        Course course = (Course) redisTemplate.opsForValue().get(courseKey);

        if (course == null) {
            course = courseMapper.selectById(courseId);
            if (course != null) {
                redisTemplate.opsForValue().set(courseKey, course, 3600, TimeUnit.SECONDS);
            }
        }

        return course;
    }

    /**
     * 生成订单号
     *
     * @return 订单号
     */
    private String generateOrderNo() {
        return "ORD" + System.currentTimeMillis() + String.format("%04d", (int) (Math.random() * 10000));
    }

    /**
     * 生成支付单号
     *
     * @return 支付单号
     */
    private String generatePaymentNo() {
        return "PAY" + System.currentTimeMillis() + String.format("%04d", (int) (Math.random() * 10000));
    }

    /**
     * 实体转换为VO
     *
     * @param order 订单实体
     * @return 订单VO
     */
    private OrderVO convertToVO(Order order) {
        OrderVO vo = new OrderVO();
        vo.setOrderId(order.getId());
        vo.setOrderNo(order.getOrderNo());
        vo.setCourseTitle(order.getCourseTitle());
        vo.setCourseCover(order.getCourseCover());
        vo.setAmount(order.getPayAmount());
        vo.setStatus(order.getStatus().getCode());
        vo.setCreateTime(order.getCreateTime());
        vo.setExpireTime(order.getExpireTime());
        return vo;
    }
}